﻿namespace Gemli.Data
{
    /// <summary>
    /// A condition or filter that is used in a <see cref="DataModelQuery&lt;T&gt;"/>.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class DataModelQueryCondition<T> : DataModelQueryCondition where T : DataModel
    {
        /// <summary>
        /// Constructs a QueryCondition&lt;T&gt; object using the specified
        /// parameters for describing the nature and for binding to the query.
        /// </summary>
        /// <param name="mapBy"></param>
        /// <param name="query"></param>
// ReSharper disable SuggestBaseTypeForParameter
        public DataModelQueryCondition(FieldMappingKeyType mapBy, DataModelQuery<T> query)
// ReSharper restore SuggestBaseTypeForParameter
            : base(mapBy, query)
        {
        }

        /// <summary>
        /// Gets or sets the metadata object that describes the 
        /// entity's database field mapping which is used
        /// to construct this condition in the appropriate database
        /// context.
        /// </summary>
        protected internal override DataModelColumnAttribute FieldMap
        {
            get
            {
                if (_fieldMap == null)
                {
                    if (this.FindFieldMappingBy == FieldMappingKeyType.ClrMember)
                        _fieldMap = DataModel.GetMapping<T>().ColumnMappings[this.FieldMappingKey];
                    else
                    {
                        var map = DataModelMap.GetEntityMapping(typeof (T));
                        foreach (var fieldmap in map.ColumnMappings)
                        {
                            if (fieldmap.Value.ColumnName == this.FieldMappingKey)
                            {
                                _fieldMap = fieldmap.Value;
                                break;
                            }
                        }
                    }
                }
                return _fieldMap;
            }
            set
            {
                _fieldMap = value;
            }
        }


        /// <summary>
        /// Used internally for binding the syntactic sugar of
        /// the associated Query's Where clauses.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public new DataModelQueryCondition<T> this[string key]
        {
            get
            {
                return (DataModelQueryCondition<T>) base[key];
            }
        }

        /// <summary>
        /// Clears out the internal reference to the originating <see cref="DataModelQuery"/>
        /// to remove the circular reference
        /// and then returns that Query to the caller.
        /// </summary>
        /// <returns></returns>
        protected new DataModelQuery<T> NullifyAndReturnQuery()
        {
            return (DataModelQuery<T>) base.NullifyAndReturnQuery();
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is equal to the specified <paramref name="value"/>.
        /// Note: This returns a Query object for syntax chaining purposes.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public new DataModelQuery<T> IsEqualTo(object value)
        {
            return (DataModelQuery<T>) base.IsEqualTo(value);
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is not equal to the specified <paramref name="value"/>.
        /// Note: This returns a Query object for syntax chaining purposes.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public new DataModelQuery<T> IsNotEqualTo(object value)
        {
            return (DataModelQuery<T>) base.IsNotEqualTo(value);
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is greater than the specified <paramref name="value"/>.
        /// Note: This returns a Query object for syntax chaining purposes.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public new DataModelQuery<T> IsGreaterThan(object value)
        {
            return (DataModelQuery<T>) base.IsGreaterThan(value);
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is greater than or equal to the specified <paramref name="value"/>.
        /// Note: This returns a Query object for syntax chaining purposes.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public new DataModelQuery<T> IsGreaterThanOrEqualTo(object value)
        {
            return (DataModelQuery<T>) base.IsGreaterThanOrEqualTo(value);
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is less than the specified <paramref name="value"/>.
        /// Note: This returns a Query object for syntax chaining purposes.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public new DataModelQuery<T> IsLessThan(object value)
        {
            return (DataModelQuery<T>) base.IsLessThan(value);
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is not equal to the specified <paramref name="value"/>.
        /// Note: This returns a Query object for syntax chaining purposes.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public new DataModelQuery<T> IsLessThanOrEqualTo(object value)
        {
            return (DataModelQuery<T>) base.IsLessThanOrEqualTo(value);
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// matches the LIKE comparison to the specified <paramref name="value"/>.
        /// Note: This returns a Query object for syntax chaining purposes.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public new DataModelQuery<T> IsLike(object value)
        {
            return (DataModelQuery<T>) base.IsLike(value);
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is null.
        /// Note: This returns a Query object for syntax chaining purposes.
        /// </summary>
        /// <returns></returns>
        public new virtual DataModelQuery<T> IsNull()
        {
            return (DataModelQuery<T>) base.IsNull();
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is not null.
        /// Note: This returns a Query object for syntax chaining purposes.
        /// </summary>
        /// <returns></returns>
        public new virtual DataModelQuery<T> IsNotNull()
        {
            return (DataModelQuery<T>)base.IsNotNull();
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is equal to the specified <paramref name="val"/>.
        /// </summary>
        /// <param name="cond"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        public static DataModelQuery<T> operator ==(DataModelQueryCondition<T> cond, object val)
        {
            if (val == null || val == System.DBNull.Value) return cond.IsNull();
            return cond.IsEqualTo(val);
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is not equal to the specified <paramref name="val"/>.
        /// </summary>
        /// <param name="cond"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        public static DataModelQuery<T> operator !=(DataModelQueryCondition<T> cond, object val)
        {
            if (val == null || val == System.DBNull.Value) return cond.IsNotNull();
            return cond.IsNotEqualTo(val);
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is greater than the specified <paramref name="val"/>.
        /// </summary>
        /// <param name="cond"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        public static DataModelQuery<T> operator >(DataModelQueryCondition<T> cond, object val)
        {
            return cond.IsGreaterThan(val);
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is less than the specified <paramref name="val"/>.
        /// </summary>
        /// <param name="cond"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        public static DataModelQuery<T> operator <(DataModelQueryCondition<T> cond, object val)
        {
            return cond.IsLessThan(val);
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is greater than or equal to the specified <paramref name="val"/>.
        /// </summary>
        /// <param name="cond"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        public static DataModelQuery<T> operator >=(DataModelQueryCondition<T> cond, object val)
        {
            return cond.IsGreaterThanOrEqualTo(val);
        }

        /// <summary>
        /// Specifies a condition where a field value 
        /// is less than or equal to the specified <paramref name="val"/>.
        /// </summary>
        /// <param name="cond"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        public static DataModelQuery<T> operator <=(DataModelQueryCondition<T> cond, object val)
        {
            return cond.IsLessThanOrEqualTo(val);
        }

        ///<summary>
        ///</summary>
        ///<param name="other"></param>
        ///<returns></returns>
        public bool Equals(DataModelQueryCondition<T> other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Equals(other._fieldMap, _fieldMap);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (DataModelQueryCondition<T>)) return false;
            return Equals((DataModelQueryCondition<T>) obj);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return (_fieldMap != null ? _fieldMap.GetHashCode() : 0);
        }
    }
}
